/*
 * Copyright 2003-2007 (c) Live Media Pty Ltd. <argot@einet.com.au> 
 *
 * This software is licensed under the Argot Public License 
 * which may be found in the file LICENSE distributed 
 * with this software.
 *
 * More information about this license can be found at
 * http://www.einet.com.au/License
 * 
 * The Developer of this software is Live Media Pty Ltd,
 * PO Box 4591, Melbourne 3001, Australia.  The license is subject 
 * to the law of Victoria, Australia, and subject to exclusive 
 * jurisdiction of the Victorian courts.
 */

package com.colony.concurrent;

/**
 *  An Event Semaphore which allows a thread to waitSem() until post() has been called.
 *  If an EventSem has been posted it must be reset before waitSem() can be called again.
 */

public class EventSem
{

   private boolean _set;
   private long _postCount;   
   private EventSem[] _cascades;
   private int _cascadeCount;

   public EventSem()
   {
      _set = true;
      _postCount = 0;
      _cascades = new EventSem[50];
      _cascadeCount = 0;
   }

   /**
    * trigger the waiting threads to wake up.
    */

   public void post()
   {
      synchronized ( this )
      {
         if ( _set == true )
            _set = false;
         notifyAll();
 
         _postCount++;

         if ( _cascadeCount > 0 )
         {
            for (int x = 0 ; x<_cascadeCount ; x++ )
            {
               _cascades[ x ].post();
            }
         }         
      }
   }

   /**
    * wait for semaphore with a timeout
    */

   public void waitSem( long timeout )
   {
      synchronized( this )
      {
         if ( _set == false ) 
         {
	    return;
         }
         try
         {
           if ( timeout > 0 )
             wait( timeout );
           else
             wait();
         }
         catch( InterruptedException ex )
         {
            System.out.println( "WaitSem timeout: " + ex.toString() );
         }
      }
   }

   /**
    *  wait for semaphore without a timeout
    */

   public void waitSem()
   {
      synchronized( this )
      {
         if ( _set == false ) 
         {  
            return;
         }
         try
         {
            wait();
         }
         catch( InterruptedException ex )
         {
            System.out.println( "WaitSem timeout: " + ex.toString() );
         }
      }
   }

   /**
    *  Reset the semaphore to allow waitSem() to be called again.
    */

   public long reset()
   {
      long postCount;

      synchronized( this )
      {
         _set = true;
         postCount = _postCount;
         _postCount= 0;
      }

      return postCount;
   }

   public long query()
   {
      synchronized( this )
      {
         return _postCount;
      }
   }

   /**
    *  addCascade is used by MuxSem to allow a post on this sem to cause a post
    *  on additional sems aswell.
    */   

   protected void addCascade( EventSem sem )
   {
      synchronized( this )
      {
         _cascades[ _cascadeCount ] = sem;
         _cascadeCount++;
      }
   }

   /**
    *  removeSem is used by MuxSem to remove a semaphore which would be posted
    *  when this is posted.
    */

   protected void removeCascade( EventSem sem )
   {
      int y = 0;
      int x = 0;

      synchronized( this )
      {
         for( x=0 ; x< _cascadeCount ; x++ ) {
            // If we find the handle, don't increment y, so that next time round
            // it gets clobbered by the handles right of it.
            if ( _cascades[x] != sem ) {
               _cascades[y] = _cascades[x];
               y++;
            } 
         }
         // If sem has been removed, then y should not equal x.
         if ( x != y ) _cascadeCount--;   
      }
   }
}